{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 1\n",
    "\n",
    "Немного переделал условия задачи, так как в исходном задании было непонятно как классифицировать фильмы с рейтингом от 4.0 до 4.5. Классификация фильмов предполагает рассчет средней оценки каждого фильма, но об этом явно не сказано. Если выставлять категорию по текущей оценке фильма, то получается, что мы классифицируем не фильмы, а классифицируем оценки к фильмам. А средняя оценка по фильму может попадать в диапазон > 4.0 и < 4.5 и эти фильмы по исходным данным ни в какую категорию не попадут.\n",
    "\n",
    "Напишите функцию, которая классифицирует фильмы из материалов занятия по следующим правилам:\n",
    "\n",
    "- оценка 2 и меньше - низкий рейтинг\n",
    "- оценка 4 и меньше - средний рейтинг\n",
    "- оценка 4.5 и меньше - высокий рейтинг\n",
    "- оценка 5 и меньше - сверхвысокий рейтинг\n",
    "\n",
    "Для выставления качественной оценки требуется посчитать среднюю оценку для фильма и записать его в стобце movie_avg_rating\n",
    "\n",
    "Результат качественной классификации запишите в столбец class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import re\n",
    "from types import MappingProxyType"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Таблица с рейтингами фильмов\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>title</th>\n",
       "      <th>genres</th>\n",
       "      <th>userId</th>\n",
       "      <th>rating</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>movieId</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toy Story (1995)</td>\n",
       "      <td>Adventure|Animation|Children|Comedy|Fantasy</td>\n",
       "      <td>7</td>\n",
       "      <td>3.0</td>\n",
       "      <td>851866703</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toy Story (1995)</td>\n",
       "      <td>Adventure|Animation|Children|Comedy|Fantasy</td>\n",
       "      <td>9</td>\n",
       "      <td>4.0</td>\n",
       "      <td>938629179</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toy Story (1995)</td>\n",
       "      <td>Adventure|Animation|Children|Comedy|Fantasy</td>\n",
       "      <td>13</td>\n",
       "      <td>5.0</td>\n",
       "      <td>1331380058</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toy Story (1995)</td>\n",
       "      <td>Adventure|Animation|Children|Comedy|Fantasy</td>\n",
       "      <td>15</td>\n",
       "      <td>2.0</td>\n",
       "      <td>997938310</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toy Story (1995)</td>\n",
       "      <td>Adventure|Animation|Children|Comedy|Fantasy</td>\n",
       "      <td>19</td>\n",
       "      <td>3.0</td>\n",
       "      <td>855190091</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                    title                                       genres  \\\n",
       "movieId                                                                  \n",
       "1        Toy Story (1995)  Adventure|Animation|Children|Comedy|Fantasy   \n",
       "1        Toy Story (1995)  Adventure|Animation|Children|Comedy|Fantasy   \n",
       "1        Toy Story (1995)  Adventure|Animation|Children|Comedy|Fantasy   \n",
       "1        Toy Story (1995)  Adventure|Animation|Children|Comedy|Fantasy   \n",
       "1        Toy Story (1995)  Adventure|Animation|Children|Comedy|Fantasy   \n",
       "\n",
       "         userId  rating   timestamp  \n",
       "movieId                              \n",
       "1             7     3.0   851866703  \n",
       "1             9     4.0   938629179  \n",
       "1            13     5.0  1331380058  \n",
       "1            15     2.0   997938310  \n",
       "1            19     3.0   855190091  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Формируем датафрейм для файла movies.csv, в индекс отправляем столбцец movieId\n",
    "df_movies = pd.read_csv('ml-latest-small/movies.csv', index_col='movieId')\n",
    "\n",
    "# Формируем датафрейм для файла ratings.csv, в индекс отправляем столбцец movieId\n",
    "df_ratings = pd.read_csv('ml-latest-small/ratings.csv', index_col='movieId')\n",
    "\n",
    "# Объединяем датафреймы с фильмами и рейтингами по индексу movieId\n",
    "# Так как нам нужны только те фильмы, у которых есть рейтинг, то для объединения выбираем метод inner\n",
    "# Объединяем по индексу, так как в индексе находятся равные идентификаторы фильмов\n",
    "df_movies_ratings = pd.merge(df_movies, df_ratings, right_index=True, left_index=True)\n",
    "\n",
    "print('Таблица с рейтингами фильмов')\n",
    "display(df_movies_ratings[:5])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def set_rating_category(elem, column_name:str='ratings') -> str:\n",
    "    \"\"\"\n",
    "    Функция для определения категории рейтинга исходя из его оценки:\n",
    "        - оценка 2 и меньше - низкий рейтинг\n",
    "        - оценка 4 и меньше - средний рейтинг\n",
    "        - оценка 4.5 и меньше - высокий рейтинг\n",
    "        - оценка 5 и меньше - сверхвысокий рейтинг\n",
    "        \n",
    "    Предполагается, что перед использованием функции в датафрейме уже сделали группировку по фильмам \n",
    "    и усреднили оценку. Функцию можно применять к отдельному столбцу или строке датафрейма.\n",
    "        \n",
    "    Входные параметры:\n",
    "        elem -- строка датафрейма с типом Series или значение столбца с типом int или float\n",
    "        column_name -- название индекса, в котором хранятся значения рейтинга. Используется только \n",
    "                       в случае значения elem в формате Series\n",
    "                       \n",
    "    Возвращаемые значения:\n",
    "        категория рейтинга фильма, тип str\n",
    "        \n",
    "    Виды категорий рейтинга:\n",
    "        - low\n",
    "        - middle\n",
    "        - high\n",
    "        - awesome\n",
    "        - пустая строка в случае ошибки\n",
    "    \"\"\"\n",
    "    \n",
    "    elem_is_Series = isinstance(elem, pd.core.series.Series)\n",
    "    elem_is_number = isinstance(elem, int) or isinstance(elem, float)\n",
    "    \n",
    "    if (not elem_is_Series and not elem_is_number):\n",
    "        raise TypeError(\"Параметр elem должен иметь тип int, float или pandas.core.series.Series\") \n",
    "    \n",
    "    # Переменную column_name преобразуем в строку, так как функция ожидает здесь значение str\n",
    "    column_name = str(column_name)\n",
    "        \n",
    "    # Создаем переменную для анализа в завивимости от типа параметра elem\n",
    "    if elem_is_number:\n",
    "        rating_value = elem\n",
    "    else:\n",
    "        if column_name not in elem.index:\n",
    "            raise ValueError(f\"Значение column_name='{column_name}' отсутствует в индексе объекта elem\")\n",
    "        rating_value = elem[column_name]\n",
    "        \n",
    "    if 0 <= rating_value <= 2:\n",
    "        return 'low'\n",
    "    if 2 < rating_value <=4:\n",
    "        return 'middle'\n",
    "    if 4 < rating_value <=4.5:\n",
    "        return 'high'\n",
    "    if 4.5 < rating_value <=5:\n",
    "        return 'awesome' \n",
    "    \n",
    "    # Если rating_value не входит в диапазон от 0 до 5, считаем это некорректным и возвращаем пустую строку\n",
    "    return ''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# рассчитаем среднюю оценку для каждого фильма и запишем ее в столбец avg_rating\n",
    "df_movies_ratings['movie_avg_rating'] = df_movies_ratings.groupby(by='movieId').mean()['rating']\n",
    "\n",
    "# На основе средней оценки получим категорию рейтинга для каждого фильма\n",
    "df_movies_ratings['class'] = df_movies_ratings.apply(set_rating_category, axis=1, \n",
    "                                                     column_name='movie_avg_rating')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Не найдено ошибок при создании категории рейтинга фильма в столбце class\n"
     ]
    }
   ],
   "source": [
    "# Проверяем были ли ошибки во время определении категории рейтнига\n",
    "# В случае возникновения ошибок категория будет заполнена пустой строкой\n",
    "class_errors_count = len(df_movies_ratings[df_movies_ratings['class'] == ''])\n",
    "if class_errors_count == 0:\n",
    "    print('Не найдено ошибок при создании категории рейтинга фильма в столбце class')\n",
    "else:\n",
    "    print(f'Кол-во ошибок формирования категории рейтинга фильма: {class_errors_count}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>title</th>\n",
       "      <th>movie_avg_rating</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>movieId</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toy Story (1995)</td>\n",
       "      <td>3.872470</td>\n",
       "      <td>middle</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Jumanji (1995)</td>\n",
       "      <td>3.401869</td>\n",
       "      <td>middle</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Grumpier Old Men (1995)</td>\n",
       "      <td>3.161017</td>\n",
       "      <td>middle</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Waiting to Exhale (1995)</td>\n",
       "      <td>2.384615</td>\n",
       "      <td>middle</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Father of the Bride Part II (1995)</td>\n",
       "      <td>3.267857</td>\n",
       "      <td>middle</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>Othello (1995)</td>\n",
       "      <td>4.100000</td>\n",
       "      <td>high</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>Persuasion (1995)</td>\n",
       "      <td>4.083333</td>\n",
       "      <td>high</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>City of Lost Children, The (Cité des enfants p...</td>\n",
       "      <td>4.025000</td>\n",
       "      <td>high</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30</th>\n",
       "      <td>Shanghai Triad (Yao a yao yao dao waipo qiao) ...</td>\n",
       "      <td>4.050000</td>\n",
       "      <td>high</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>37</th>\n",
       "      <td>Across the Sea of Time (1995)</td>\n",
       "      <td>2.000000</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>38</th>\n",
       "      <td>It Takes Two (1995)</td>\n",
       "      <td>2.000000</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>41</th>\n",
       "      <td>Richard III (1995)</td>\n",
       "      <td>4.021739</td>\n",
       "      <td>high</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>53</th>\n",
       "      <td>Lamerica (1994)</td>\n",
       "      <td>5.000000</td>\n",
       "      <td>awesome</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>66</th>\n",
       "      <td>Lawnmower Man 2: Beyond Cyberspace (1996)</td>\n",
       "      <td>2.000000</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>80</th>\n",
       "      <td>White Balloon, The (Badkonake sefid) (1995)</td>\n",
       "      <td>4.625000</td>\n",
       "      <td>awesome</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>87</th>\n",
       "      <td>Dunston Checks In (1996)</td>\n",
       "      <td>1.666667</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>96</th>\n",
       "      <td>In the Bleak Midwinter (1995)</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>116</th>\n",
       "      <td>Anne Frank Remembered (1995)</td>\n",
       "      <td>4.750000</td>\n",
       "      <td>awesome</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>178</th>\n",
       "      <td>Love &amp; Human Remains (1993)</td>\n",
       "      <td>4.700000</td>\n",
       "      <td>awesome</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>183</th>\n",
       "      <td>Mute Witness (1994)</td>\n",
       "      <td>5.000000</td>\n",
       "      <td>awesome</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                     title  movie_avg_rating  \\\n",
       "movieId                                                                        \n",
       "1                                         Toy Story (1995)          3.872470   \n",
       "2                                           Jumanji (1995)          3.401869   \n",
       "3                                  Grumpier Old Men (1995)          3.161017   \n",
       "4                                 Waiting to Exhale (1995)          2.384615   \n",
       "5                       Father of the Bride Part II (1995)          3.267857   \n",
       "26                                          Othello (1995)          4.100000   \n",
       "28                                       Persuasion (1995)          4.083333   \n",
       "29       City of Lost Children, The (Cité des enfants p...          4.025000   \n",
       "30       Shanghai Triad (Yao a yao yao dao waipo qiao) ...          4.050000   \n",
       "37                           Across the Sea of Time (1995)          2.000000   \n",
       "38                                     It Takes Two (1995)          2.000000   \n",
       "41                                      Richard III (1995)          4.021739   \n",
       "53                                         Lamerica (1994)          5.000000   \n",
       "66               Lawnmower Man 2: Beyond Cyberspace (1996)          2.000000   \n",
       "80             White Balloon, The (Badkonake sefid) (1995)          4.625000   \n",
       "87                                Dunston Checks In (1996)          1.666667   \n",
       "96                           In the Bleak Midwinter (1995)          1.000000   \n",
       "116                           Anne Frank Remembered (1995)          4.750000   \n",
       "178                            Love & Human Remains (1993)          4.700000   \n",
       "183                                    Mute Witness (1994)          5.000000   \n",
       "\n",
       "           class  \n",
       "movieId           \n",
       "1         middle  \n",
       "2         middle  \n",
       "3         middle  \n",
       "4         middle  \n",
       "5         middle  \n",
       "26          high  \n",
       "28          high  \n",
       "29          high  \n",
       "30          high  \n",
       "37           low  \n",
       "38           low  \n",
       "41          high  \n",
       "53       awesome  \n",
       "66           low  \n",
       "80       awesome  \n",
       "87           low  \n",
       "96           low  \n",
       "116      awesome  \n",
       "178      awesome  \n",
       "183      awesome  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Выведем по 5 фильмов из каждого класса\n",
    "display(df_movies_ratings[['title', 'movie_avg_rating', 'class']]\n",
    "        .drop_duplicates().groupby(by='class').head(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 2\n",
    "\n",
    "Используем файл keywords.csv.\n",
    "\n",
    "Необходимо написать гео-классификатор, который каждой строке сможет выставить географическую принадлежность определенному региону. Т. е. если поисковый запрос содержит название города региона, то в столбце ‘region’ пишется название этого региона. Если поисковый запрос не содержит названия города, то ставим ‘undefined’.\n",
    "\n",
    "Правила распределения по регионам Центр, Северо-Запад и Дальний Восток:\n",
    "\n",
    "```\n",
    "geo_data = {\n",
    "\n",
    "'Центр': ['москва', 'тула', 'ярославль'],\n",
    "\n",
    "'Северо-Запад': ['петербург', 'псков', 'мурманск'],\n",
    "\n",
    "'Дальний Восток': ['владивосток', 'сахалин', 'хабаровск']\n",
    "}\n",
    "```\n",
    "\n",
    "Результат классификации запишите в отдельный столбец region."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Так как ключи словаря являются хешируемым элементом и поиск по ключам осуществляется значительно быстрее, \n",
    "# чем по значениям словаря, то принято решение сделать в качестве ключей названия городов, \n",
    "# это повысит скорость поиска нужного города.\n",
    "# Чтобы функция каждый раз не создавала словарь, принято решение сделать гео-дату в виде глобальной переменной\n",
    "GEO_DATA = {\n",
    "    'москва': 'Центр',\n",
    "    'тула': 'Центр',\n",
    "    'ярославль': 'Центр',\n",
    "    'петербург': 'Северо-Запад',\n",
    "    'псков': 'Северо-Запад',\n",
    "    'мурманск': 'Северо-Запад',\n",
    "    'владивосток': 'Дальний Восток',\n",
    "    'сахалин': 'Дальний Восток',\n",
    "    'хабаровск': 'Дальний Восток',  \n",
    "}\n",
    "\n",
    "\n",
    "def geo_class(elem, geo_data, column_name:str='keyword') -> str:\n",
    "    \"\"\"\n",
    "    Функция для определения географической принадлежности строки с символами.\n",
    "    \n",
    "    Функцию можно применять к отдельному столбцу или строке датафрейма pandas.\n",
    "        \n",
    "    Входные параметры:\n",
    "        elem -- строка датафрейма с типом Series или значение столбца с типом str\n",
    "        column_name -- название индекса, в котором хранятся строковые значения для анализа. \n",
    "                       Используется только в случае значения elem в формате Series\n",
    "        geo_data -- словарь с гео-датой в формате {'название города': 'гео-признак'}\n",
    "                       \n",
    "    Возвращаемые значения:\n",
    "        географическая метка, тип str\n",
    "    \"\"\"\n",
    "    \n",
    "    elem_is_Series = isinstance(elem, pd.core.series.Series)\n",
    "    elem_is_str = isinstance(elem, str)\n",
    "    \n",
    "    if (not elem_is_Series and not elem_is_str):\n",
    "        raise TypeError(\"Параметр elem должен иметь тип str или pandas.core.series.Series\") \n",
    "        \n",
    "    if not isinstance(geo_data, dict):\n",
    "        raise TypeError(\"Параметр geo_data должен иметь тип dict\") \n",
    "        \n",
    "    # Переменную нельзя менять внутри функции, поэтому делаем проксирование с правом только на чтение\n",
    "    geo_data = MappingProxyType(geo_data)\n",
    "    \n",
    "    # Переменную column_name преобразуем в строку, так как функция ожидает здесь значение str\n",
    "    column_name = str(column_name)\n",
    "        \n",
    "    # Создаем переменную для анализа в завивимости от типа параметра elem\n",
    "    if elem_is_str:\n",
    "        str_value = elem\n",
    "    else:\n",
    "        if column_name not in elem.index:\n",
    "            raise ValueError(f\"Значение column_name='{column_name}' отсутствует в индексе объекта elem\")\n",
    "        str_value = elem[column_name]\n",
    "  \n",
    "    # Разбиваем строку на отдельные слова\n",
    "    word_list = re.split(r'[\\.\\,\\; ]', str_value)\n",
    "    \n",
    "    city_list = geo_data.keys()\n",
    "    \n",
    "    # Ищем слова в списке городов, если находим город в ключах словаря, то выдаем значение с гео-меткой\n",
    "    for word in word_list:\n",
    "        word = word.lower().strip()\n",
    "        if word in city_list:\n",
    "            return geo_data[word]\n",
    "        \n",
    "    # Если ни одно слово не совпало с городом из словаря или word_list пустой, то возвращаем undefined\n",
    "    return 'undefined'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Формируем датафрейм с ключевыми словами\n",
    "df_keywords = pd.read_csv('keywords.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Раставляем гео-метки для ключевых слов в колонку region\n",
    "df_keywords['region'] = df_keywords.apply(geo_class, axis=1, column_name='keyword', geo_data=GEO_DATA)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>keyword</th>\n",
       "      <th>shows</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>вк</td>\n",
       "      <td>64292779</td>\n",
       "      <td>undefined</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>одноклассники</td>\n",
       "      <td>63810309</td>\n",
       "      <td>undefined</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>порно</td>\n",
       "      <td>41747114</td>\n",
       "      <td>undefined</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ютуб</td>\n",
       "      <td>39995567</td>\n",
       "      <td>undefined</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>вконтакте</td>\n",
       "      <td>21014195</td>\n",
       "      <td>undefined</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>127</th>\n",
       "      <td>авито москва</td>\n",
       "      <td>979292</td>\n",
       "      <td>Центр</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>370</th>\n",
       "      <td>авито ру санкт петербург</td>\n",
       "      <td>425134</td>\n",
       "      <td>Северо-Запад</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>849</th>\n",
       "      <td>авито ярославль</td>\n",
       "      <td>209581</td>\n",
       "      <td>Центр</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1063</th>\n",
       "      <td>фарпост владивосток</td>\n",
       "      <td>176951</td>\n",
       "      <td>Дальний Восток</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1236</th>\n",
       "      <td>банк санкт петербург</td>\n",
       "      <td>174375</td>\n",
       "      <td>Северо-Запад</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1259</th>\n",
       "      <td>москва</td>\n",
       "      <td>153050</td>\n",
       "      <td>Центр</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1346</th>\n",
       "      <td>санкт петербург</td>\n",
       "      <td>144232</td>\n",
       "      <td>Северо-Запад</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1396</th>\n",
       "      <td>гисметео москва</td>\n",
       "      <td>140279</td>\n",
       "      <td>Центр</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1482</th>\n",
       "      <td>госуслуги москва</td>\n",
       "      <td>133061</td>\n",
       "      <td>Центр</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2153</th>\n",
       "      <td>авито псков</td>\n",
       "      <td>98931</td>\n",
       "      <td>Северо-Запад</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2175</th>\n",
       "      <td>дром хабаровск</td>\n",
       "      <td>98361</td>\n",
       "      <td>Дальний Восток</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2398</th>\n",
       "      <td>авито мурманск</td>\n",
       "      <td>89787</td>\n",
       "      <td>Северо-Запад</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3206</th>\n",
       "      <td>дром владивосток</td>\n",
       "      <td>73084</td>\n",
       "      <td>Дальний Восток</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3275</th>\n",
       "      <td>100 сп владивосток</td>\n",
       "      <td>76418</td>\n",
       "      <td>Дальний Восток</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3687</th>\n",
       "      <td>авито хабаровск</td>\n",
       "      <td>63736</td>\n",
       "      <td>Дальний Восток</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                       keyword     shows          region\n",
       "0                           вк  64292779       undefined\n",
       "1                одноклассники  63810309       undefined\n",
       "2                        порно  41747114       undefined\n",
       "3                         ютуб  39995567       undefined\n",
       "4                    вконтакте  21014195       undefined\n",
       "127               авито москва    979292           Центр\n",
       "370   авито ру санкт петербург    425134    Северо-Запад\n",
       "849            авито ярославль    209581           Центр\n",
       "1063       фарпост владивосток    176951  Дальний Восток\n",
       "1236      банк санкт петербург    174375    Северо-Запад\n",
       "1259                    москва    153050           Центр\n",
       "1346           санкт петербург    144232    Северо-Запад\n",
       "1396           гисметео москва    140279           Центр\n",
       "1482          госуслуги москва    133061           Центр\n",
       "2153               авито псков     98931    Северо-Запад\n",
       "2175            дром хабаровск     98361  Дальний Восток\n",
       "2398            авито мурманск     89787    Северо-Запад\n",
       "3206          дром владивосток     73084  Дальний Восток\n",
       "3275        100 сп владивосток     76418  Дальний Восток\n",
       "3687           авито хабаровск     63736  Дальний Восток"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Выведем по 5 ключевых слов из каждой категории\n",
    "display(df_keywords.groupby(by='region').head(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 3 (бонусное)\n",
    "\n",
    "Есть мнение, что “раньше снимали настоящее кино, не то что сейчас”. Ваша задача проверить это утверждение, используя файлы с рейтингами фильмов из прошлого домашнего занятия (файл ratings.csv из базы https://grouplens.org/datasets/movielens). Т. е. проверить верно ли, что с ростом года выпуска фильма его средний рейтинг становится ниже.\n",
    "\n",
    "При этом мы не будем затрагивать субьективные факторы выставления этих рейтингов, а пройдемся по следующему алгоритму:\n",
    "\n",
    "1. В переменную years запишите список из всех годов с 1950 по 2010.\n",
    "\n",
    "2. Напишите функцию production_year, которая каждой строке из названия фильма выставляет год выпуска. Не все названия фильмов содержат год выпуска в одинаковом формате, поэтому используйте следующий алгоритм:\n",
    "\n",
    "    2.1 для каждой строки пройдите по всем годам списка years\n",
    "    \n",
    "    2.2 если номер года присутствует в названии фильма, то функция возвращает этот год как год выпуска\n",
    "    \n",
    "    2.3 если ни один из номеров года списка years не встретился в названии фильма, то возвращается 1900 год\n",
    "    \n",
    "3. Запишите год выпуска фильма по алгоритму пункта 2 в новый столбец ‘year’\n",
    "\n",
    "4. Посчитайте средний рейтинг всех фильмов для каждого значения столбца ‘year’ и отсортируйте результат по убыванию рейтинга"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Немного переделал алгоритм:\n",
    "\n",
    "1. Напишите функцию production_year, которая каждой строке из названия фильма выставляет год выпуска. Предполагаем, что год выпуска указан в формате (YYYY) и может находиться в любой части названия фильма. Если найдено больше одного вхождения формата года или не найдено ничего, то функция должна вернуть год 1700.\n",
    "2. Запишите год выпуска фильма по алгоритму пункта 1 в новый столбец ‘year’\n",
    "3. Посчитайте средний рейтинг всех фильмов для каждого значения столбца ‘year’ и отсортируйте результат по убыванию рейтинга"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_movie_year(elem, column_name=None):\n",
    "    \"\"\"\n",
    "    Функция определяет год выпуска фильма с помощью анализа названия фильма.\n",
    "    \n",
    "    Функцию можно применять к отдельному столбцу или строке датафрейма pandas.\n",
    "    \n",
    "    Входные параметры:\n",
    "        elem -- строка датафрейма с типом Series или значение столбца с типом str\n",
    "        column_name -- название индекса, в котором хранятся значения рейтинга. Используется только \n",
    "                       в случае значения elem в формате Series\n",
    "    \"\"\"\n",
    "    \n",
    "    elem_is_str = isinstance(elem, str)\n",
    "    elem_is_Series = isinstance(elem, pd.core.series.Series)\n",
    "    \n",
    "    if (not elem_is_Series and not elem_is_str):\n",
    "        raise TypeError(\"Параметр elem должен иметь тип str или pandas.core.series.Series\") \n",
    "    \n",
    "    # Переменную column_name преобразуем в строку, так как функция ожидает здесь значение str\n",
    "    column_name = str(column_name)\n",
    "    \n",
    "    # Создаем переменную для анализа в завивимости от типа параметра elem\n",
    "    if elem_is_str:\n",
    "        movie_name = elem\n",
    "    elif elem_is_Series:\n",
    "        if column_name not in elem.index:\n",
    "            raise ValueError(f\"Значение column_name='{column_name}' отсутствует в индексе объекта elem\")\n",
    "        movie_name = elem[column_name]\n",
    "    \n",
    "    #Опытным путем выявлено, что меньше всего ошибок возникает, если искать год в формате (YYYY)\n",
    "    movie_years_list = re.compile(r'\\((\\d{4})\\)').findall(movie_name)\n",
    "    \n",
    "    # Если найдено более двух годов в имени фильма или не найдено ничего, то возвращаем 1990 год.\n",
    "    if len(movie_years_list) > 1 or len(movie_years_list) == 0:\n",
    "        return 1700\n",
    "    \n",
    "    return int(movie_years_list[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Для каждого фильма найдем год фильма и запишем его в столбец year\n",
    "df_movies_ratings['year'] = df_movies_ratings.title.apply(get_movie_year)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Формируем отдельный датафрейм с индексом year и столбцом rating со средним рейтингом всех фильмов по году\n",
    "df_year_movie_ratings = df_movies_ratings[['year', 'rating']].groupby('year').mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>rating</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>year</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1957</th>\n",
       "      <td>4.014241</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1972</th>\n",
       "      <td>4.011136</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1952</th>\n",
       "      <td>4.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1954</th>\n",
       "      <td>3.994220</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1951</th>\n",
       "      <td>3.983539</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1974</th>\n",
       "      <td>3.978704</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1962</th>\n",
       "      <td>3.952446</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1977</th>\n",
       "      <td>3.905786</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1964</th>\n",
       "      <td>3.841492</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1959</th>\n",
       "      <td>3.841033</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1958</th>\n",
       "      <td>3.823171</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1975</th>\n",
       "      <td>3.821629</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1969</th>\n",
       "      <td>3.817376</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1967</th>\n",
       "      <td>3.814516</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1960</th>\n",
       "      <td>3.809211</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1976</th>\n",
       "      <td>3.806794</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1973</th>\n",
       "      <td>3.806667</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1979</th>\n",
       "      <td>3.777966</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1981</th>\n",
       "      <td>3.758763</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1953</th>\n",
       "      <td>3.754587</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        rating\n",
       "year          \n",
       "1957  4.014241\n",
       "1972  4.011136\n",
       "1952  4.000000\n",
       "1954  3.994220\n",
       "1951  3.983539\n",
       "1974  3.978704\n",
       "1962  3.952446\n",
       "1977  3.905786\n",
       "1964  3.841492\n",
       "1959  3.841033\n",
       "1958  3.823171\n",
       "1975  3.821629\n",
       "1969  3.817376\n",
       "1967  3.814516\n",
       "1960  3.809211\n",
       "1976  3.806794\n",
       "1973  3.806667\n",
       "1979  3.777966\n",
       "1981  3.758763\n",
       "1953  3.754587"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Топ-20 годов из диапазона от 1950 по текущий, в которые были сняты фильмы с наилучшими рейтингами\n",
    "display(df_year_movie_ratings[df_year_movie_ratings.index > 1950].sort_values('rating', ascending=False)[:20])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
